package fr.lille.inria.adam.lifl.EcoGardeInventory.Utils;

import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.util.Log;

public class SQLiteDumper {
	
	/**Effectue un dump du Cursor. Le Cursor doit être positionné
	 * à previousFirst sinon le résultat sera vide.
	 * Le Cursor est rendu à sa position initiale.
	 * @param c
	 * @return
	 */
	public static String toString(Cursor c) {
		if ( ! c.isBeforeFirst()) return "";
		final String result = arrayToString(toArray(c));
		c.move(-1);
		return result;
	}
	
	/**Effectue un dump (pretty-formated) d'une table
	 * @param db
	 * @param tableName
	 * @return
	 */
	public static String toString(SQLiteDatabase db, String tableName) {
		return arrayToString(toArray(db, tableName));
	}
	
	public static String toString(SQLiteDatabase db) {
		StringBuilder sb = new StringBuilder();
		String[] dbNames = getTableNames(db);
		for (String aTableName : dbNames) {
			sb.append("TABLE NAME: ").append(aTableName).append('\n')
			.append(toString(db, aTableName)).append("\n\n");
		}
		return sb.toString();
	}
	
	public static String[] toStringPerTable(SQLiteDatabase db) {
		String[] dbNames = getTableNames(db);
		String[] result = new String[dbNames.length];
		for (int i = 0; i < dbNames.length; i++) {
			StringBuilder sb = new StringBuilder();
			String aTableName = dbNames[i];
			sb.append("TABLE NAME: ").append(aTableName).append('\n')
			.append(toString(db, aTableName)).append("\n");
			result[i] = sb.toString();
		}
		return result;
	}
	
	/**Extrait tout les libellés de champs et toutes les données 
	 * d'une table donnée
	 * @param db 
	 * @param tableName La table
	 * @return La première ligne contient le nom des champs. Les autres lignes 
	 * contiennent les données
	 */
	public static String[][] toArray(SQLiteDatabase db, String tableName) {
		return toArray(db.query(tableName, null, null, null, null, null, null));
	}
	
	/**
	 * @param db
	 * @param c
	 * @return
	 */
	public static String[][] toArray(Cursor c) {
		String[][] data = new String[c.getCount()+1][c.getColumnCount()];
		for (int itCol=0; itCol<c.getColumnCount(); itCol++) data[0][itCol] = c.getColumnName(itCol);
		
		int itRow = 1;
		while (c.moveToNext()) {
			for (int itCol=0; itCol<c.getColumnCount(); itCol++) {
				String v = c.getString(itCol);
				data[itRow][itCol] = (v==null)?"@null":v;
			}
			itRow++;
		}
		
		return data;
	}
	
	private static String arrayToString(String[][] data) {
		int[] maxColumnsLength = computeMaxColumnsLength(data);
		
		StringBuilder sb = new StringBuilder();
		sb.append("| ");
		for (int itCol = 0; itCol < data[0].length; itCol++) {
			String s = data[0][itCol];
			sb.append(s).append(getChars(" ", maxColumnsLength[itCol]-s.length())).append(" | ");
		}
		final String spacer = getChars("-", sb.length()-2);
		sb.append('\n').append(spacer).append('\n');
		
		for (int itRow = 0; itRow < data.length; itRow++) {
			sb.append("| ");
			for (int itCol = 0; itCol < data[itRow].length; itCol++) {
				String s = data[itRow][itCol];
				sb.append(s).append(getChars(" ", maxColumnsLength[itCol]-s.length())).append(" | ");
			}
			sb.append('\n');
		}
		sb.append(spacer).append('\n');
		
		return sb.toString();
	}
	
	/**Cette renvoie une chaine de caractère de longueur length qui est
	 * la chaine c avec des espaces à sa suite.
	 * @param c La chaine à étendre
	 * @param length La longueur finale de la chaine
	 * @return
	 */
	public final static String getChars(String c, int length) {
		if (length==0) return "";
		else if (length==1) return c;
		else return getChars(c, length-1, c);
	}
	
	/**
	 * @param c La chaine qui sera situé au début de la chaine retournée
	 * @param length La longueur finale de la chaine à retourner
	 * @param work Les espaces déjà calculés
	 * @return
	 */
	private final static String getChars(String c, int length, String work) {	
		if (length==0) return work;
		if (work.length()<=length) return getChars(c, length-work.length(), work+work);
		else return work+getChars(c, length-1, c);
	}

	/**Pour chaque colonne, renvoie la taille de plus grande chaine de caractère.
	 * @param data
	 * @return
	 */
	public final static int[] computeMaxColumnsLength(String[][] data) {
		int[] maxColumnsLength = new int[data[0].length];
		for (int itCol = 0; itCol < maxColumnsLength.length; itCol++) maxColumnsLength[itCol]=0;
		
		for (int itRow = 0; itRow < data.length; itRow++) {
			for (int itCol = 0; itCol < data[itRow].length; itCol++) {
				String v = data[itRow][itCol];
				maxColumnsLength[itCol] = Math.max(maxColumnsLength[itCol], v.length());
			}
		}
		
		return maxColumnsLength;
	}
	
	/**
	 * @author http://stackoverflow.com/questions/6177598/how-to-get-the-names-of-all-tables-present-in-a-database-in-android-sql-lite
	 * @param db
	 * @return
	 */
	public static String[] getTableNames(SQLiteDatabase db){
	    try {
	        StringBuilder sb = new StringBuilder();
	        sb.append("SELECT name FROM sqlite_master "); 
	        sb.append("WHERE type IN ('table','view') AND name NOT LIKE 'sqlite_%' ");
	        sb.append("UNION ALL ");
	        sb.append("SELECT name FROM sqlite_temp_master "); 
	        sb.append("WHERE type IN ('table','view') ");
	        sb.append("ORDER BY 1");

	        Cursor c = db.rawQuery("SELECT name FROM sqlite_master WHERE name NOT LIKE 'sqlite_%' AND name NOT LIKE 'android_metadata' ORDER BY name", null);

	        String[] result = new String[c.getCount()];
	        int i = 0;
	        while (c.moveToNext()) {
	            result[i]= c.getString(c.getColumnIndex("name"));
	            i++;
	        }
	        c.close();
	        return result;
	    }
	    catch(SQLiteException e){
	        Log.e("SQLiteDumper", "Dump tablename error", e);
	        return new String[0];
	    }
	    
	}
}
